import logging
from typing import Callable, Any, Union
from datetime import timedelta

import aioredis
from aioredis import ConnectionPool
from afast_core.core_libs.redis_cache.utils import pickle_encoder, pickle_decoder, DEFAULT_NAMESPACE, DEFAULT_PREFIX,key_builder

log = logging.getLogger(__name__)


class AsynioRedisCache:
    # 指向自身的实例对象
    instance = None
    def __init__(
            self,
            password: str = None,
            db: int = 0,
            host: str = "localhost",
            port: int = 6379,
            decode_responses=True,
            encoder: Callable[..., Any] = pickle_encoder,
            decoder: Callable[..., Any] = pickle_decoder,
            namespace: str = DEFAULT_NAMESPACE,
            key_prefix: str = DEFAULT_PREFIX,
            key_builder: Callable[..., Any] = key_builder,
            **redis_kwargs,
    ) -> None:
        pool = ConnectionPool(host=host,
                              password=password,
                              db=db,
                              port=port,
                              decode_responses=decode_responses,
                              **redis_kwargs,
                              )
        self.redis = aioredis.Redis(connection_pool=pool)

        self.encoder = encoder
        self.decoder = decoder
        self.namespace = namespace
        self.key_prefix = key_prefix
        self.key_builder = key_builder
        # 给全局静态变量赋值自身
        AsynioRedisCache.instance = self
        log.info("redis intialized")
        # log.info(f"send ping to redis {self.redis.ping()}")

    async def ping(self):
        return await self.redis.ping()

    async def set_key(self, key, val, exp: Union[timedelta, int] = None, isencoder=False):
        if exp:
            if isencoder:
                val = self.encoder(val)
            await self.redis.set(key, val, ex=exp)
        else:
            await self.redis.set(key, val)

    async def get_key(self, key: str, isencoder=False):
        val = await self.redis.get(key)
        if val:
            if isencoder:
                val = self.decoder(val)
        return val

    async def close_connections(self):
        await self.redis.close()



    @classmethod
    def get_instance(cls):
        if cls.instance:
            return cls.instance
        raise Exception("You mus be initialize redis first")

    @classmethod
    async def clear_keys(cls, pattern: str):
        ns_keys = pattern + "*"
        scan_iter_reslut = await cls.instance.redis.scan_iter(match=ns_keys)
        for key in scan_iter_reslut:
            # print(key)
            if key:
                # print("find")
                await cls.instance.redis.delete(key)
        return True

    @classmethod
    def init(
            cls,
            password: str = None,
            db: int = 0,
            host: str = "localhost",
            port: int = 6379,
            encoder: Callable[..., Any] = pickle_encoder,
            decoder: Callable[..., Any] = pickle_decoder,
            namespace: str = DEFAULT_NAMESPACE,
            key_prefix: str = DEFAULT_PREFIX,
            key_builder: Callable[..., Any] = key_builder,
            **kwargs,
    ):
        if not cls.instance:
            cls(
                host=host,
                port=port,
                db=db,
                password=password,
                encoder=encoder,
                decoder=decoder,
                namespace=namespace,
                key_prefix=key_prefix,
                key_builder=key_builder,
                **kwargs,
            )
